#ifndef __TABLEVIDEO__
#define __TABLEVIDEO__

#include <iostream>
#include <string>
#include <mutex>
#include <cstdlib>
#include <cstdio>
#include <cstring>
#include <pthread.h>
#include <mysql/mysql.h>
#include <jsoncpp/json/json.h>
#include "Log.hpp"
#define INSERT_SQL "insert video_tb values(null,'%s','%s','%s','%s');"
#define UPDATE_SQL "update video_tb set name = '%s',info = '%s' where id=%d;"
#define DELETE_SQL "delete from video_tb where id=%d;"

#define SELECTONCE "select* from video_tb where id=%d;"
#define SELECTALL "select* from video_tb;"
#define SELECTLIKE "select* from video_tb where name like '%%%s%%';"
namespace Table_control
{
    const static std::string username = "root";       // MySQL用户
    const static std::string password = "";           // MySQL密码
    const static std::string host = "127.0.0.1";      // 服务器的ip
    const static std::string database = "aod_system"; // 数据库名称
    using namespace LOGSpace;
    static MYSQL *Initmysql()
    {
        MYSQL *mysql = mysql_init(NULL); // 在堆上申请的句柄
        if (mysql == nullptr)
        {
            LOG(FATAL, "mysql init failed!\n");
            return nullptr;
        }
        // 连接数据库
        if (mysql_real_connect(mysql, host.c_str(), username.c_str(), password.c_str(), database.c_str(), 0, nullptr, 0) == nullptr)
        {
            LOG(FATAL, "connect database failed!\n");
            mysql_close(mysql);
            return nullptr;
        }
        // 设置字符集
        mysql_set_character_set(mysql, "utf8");
        return mysql;
    }
    // 销毁句柄
    static void MysqlDestroy(MYSQL *mysql)
    {
        if (mysql != nullptr)
        {
            mysql_close(mysql);
        }
        return;
    }
    // 执行sql语句
    static bool MysqlQuery(MYSQL *mysql, const std::string &sql)
    {
        int ret = mysql_query(mysql, sql.c_str());
        if (ret < 0)
        {
            LOG(ERROR, "query failed!,msg = %s\n", mysql_error(mysql));
            return false;
        }
        return true;
    }
    class Table_Video
    {
    public:
        // 新增视频信息
        bool Insert(const Json::Value &video)
        {
            // id、name、info、video、image
            std::string sql;
            // 扩容
            sql.resize(4096 + video["info"].asString().size());
            if (video["name"].asString().empty() || video["info"].asString().empty() || video["video"].asString().empty() || video["image"].asString().empty())
            {
                // 如果不存在
                LOG(ERROR, "insert sql failed!\n");
                return false;
            }

            sprintf(&sql[0], INSERT_SQL, video["name"].asCString(), video["info"].asCString(), video["video"].asCString(), video["image"].asCString());
            return MysqlQuery(_mysql, sql);
        }
        // 更新指定视频的信息
        bool Update(const int video_id, const Json::Value &video)
        {
            std::string sql;
            // 扩容
            sql.resize(4096 + video["info"].asString().size());
            if (video["name"].asString().empty() || video["info"].asString().empty())
            {
                // 如果不存在
                LOG(ERROR, "update sql failed!\n");
                return false;
            }

            sprintf(&sql[0], UPDATE_SQL, video["name"].asCString(), video["info"].asCString(), video_id);
            return MysqlQuery(_mysql, sql);
        }
        // 删除指定视频的信息
        bool Delete(const int video_id)
        {
            char sql[1024];
            memset(sql, 0, sizeof sql);

            sprintf(sql, DELETE_SQL, video_id);
            return MysqlQuery(_mysql, sql);
        }
        // 重新连接：
        void RetryConnectionMySQL()
        {
            MysqlDestroy(_mysql); // 先销毁
            std::cout<<"开始重连"<<std::endl;
            while (!(_mysql = Initmysql()))
            {
                LOG(INFO, "try retryconnection mysql......\n");
            }
        }
        // 处理超时
        static void *HandlerTimeOunt(void *args)
        {
            std::cout<<"开始检测！"<<std::endl;
            Table_Video *self = static_cast<Table_Video *>(args);
            while (true)
            {
                while (self->_time++ < 28800)
                {
                    sleep(1);
                }// TODO
                std::cout<<"已断开链接！"<<std::endl;
                self->RetryConnectionMySQL();
                self->_time = 0;
            }
            return nullptr;
        }
        // 查看指定视频的信息
        bool SelectOnce(const int video_id, Json::Value *video)
        {
            char sql[1024];
            memset(sql, 0, sizeof sql);
            sprintf(sql, SELECTONCE, video_id);
            std::cout << "sql语句:" << sql << std::endl;
            _mutex.lock();
            MysqlQuery(_mysql, sql);
            // 保存结果集到本地
            MYSQL_RES *res = mysql_store_result(_mysql);
            if (res == nullptr)
            {
                LOG(ERROR, "store ret to local failed!\n");
                _mutex.unlock();
                return false;
            }
            _mutex.unlock();
            int res_row = mysql_num_rows(res);
            if (res_row != 1)
            {
                LOG(ERROR, "data fetch to local failed!\n");
                return false;
            }

            MYSQL_ROW row = mysql_fetch_row(res);
            (*video)["id"] = video_id;
            (*video)["name"] = row[1];
            (*video)["info"] = row[2];
            (*video)["video"] = row[3];
            (*video)["image"] = row[4];
            mysql_free_result(res);
            return true;
        }
        // 查看全部视频的信息
        bool SelectAll(Json::Value *videos)
        {
            Json::Value values;
            _mutex.lock();
            if (!MysqlQuery(_mysql, SELECTALL))
            {
                _mutex.unlock();
                return false;
            }
            // 保存结果集到本地
            MYSQL_RES *res = mysql_store_result(_mysql);
            if (res == nullptr)
            {
                LOG(ERROR, "store ret to local failed!\n");
                _mutex.unlock();
                return false;
            }
            _mutex.unlock();
            int res_row = mysql_num_rows(res);
            for (int i = 0; i < res_row; i++)
            {
                MYSQL_ROW row = mysql_fetch_row(res);
                values["id"] = atoi(row[0]);
                values["name"] = row[1];
                values["info"] = row[2];
                values["video"] = row[3];
                values["image"] = row[4];
                videos->append(values);
            }
            std::cout << "搜索全部完成!" << std::endl;
            mysql_free_result(res);
            return true;
        }
        // 模糊搜索
        bool SelectLike(const std::string &key, Json::Value *videos)
        {
            char sql[1024];
            memset(sql, 0, sizeof sql);
            sprintf(sql, SELECTLIKE, key.c_str());
            std::cout << "sql语句:" << sql << std::endl;
            _mutex.lock();
            if (!MysqlQuery(_mysql, sql))
            {
                _mutex.unlock();
                return false;
            }
            // 保存结果集到本地
            MYSQL_RES *res = mysql_store_result(_mysql);
            if (res == nullptr)
            {
                LOG(ERROR, "store ret to local failed!\n");
                _mutex.unlock();
                return false;
            }
            _mutex.unlock();
            int res_row = mysql_num_rows(res);
            for (int i = 0; i < res_row; i++)
            {
                Json::Value value;
                MYSQL_ROW row = mysql_fetch_row(res);
                value["id"] = atoi(row[0]);
                value["name"] = row[1];
                value["info"] = row[2];
                value["video"] = row[3];
                value["image"] = row[4];
                videos->append(value);
            }
            std::cout << "模糊搜索完成!" << std::endl;
            mysql_free_result(res);
            return true;
        }

    public:
        static Table_Video *GetInstacne()
        {
            if (_instacne == nullptr)
            {
                _instacne = new Table_Video();
            }
            return _instacne;
        }
        Table_Video()
        {
            _mysql = Initmysql();
            if (_mysql == nullptr)
            {
                LOG(FATAL, "mysql init failed!\n");
                exit(-1);
            }
            // 线程开始一致检测连接状态
            pthread_create(&check_time_out, nullptr, HandlerTimeOunt, this);
        }
        ~Table_Video()
        {
            MysqlDestroy(_mysql);
        }

    private:
        MYSQL *_mysql;
        std::mutex _mutex;
        static Table_Video *_instacne;
        pthread_t check_time_out; // 检测mysql是否断开连接;
    private:
        int _time = 0; // 超时时间
    };
    Table_Video *Table_Video::_instacne = nullptr;
}
#endif